
#ifndef __BMANX_BOMBERLEAGUE_WORLD__HEADER_FILE__
#define __BMANX_BOMBERLEAGUE_WORLD__HEADER_FILE__

#include "coid/comm/metastream/metastream.h"
#include "coid/comm/metastream/fmtstreamcxx.h"

#include "../bmanlib/world.h"
#include "../teximage/teximage.h"

//tile width in pixels (original texture)
#define TWpx    48


////////////////////////////////////////////////////////////////////////////////
struct PlayerProfile
{
    char _key_north, _key_south, _key_east, _key_west;  ///movement keys
    char _key_a, _key_b;                                ///action keys

    uint _colpri;           ///< primary color
    uint _colsec;           ///< secondary color

    charstr _nick;
    charstr _ip;            ///< server address

    bool _drawratehalf;
    bool _fullscreen;


    void random()
    {
        time_t t;
        rnd_strong rnd( (uint)::time(&t) );

        if( _colpri == UMAX )
            _colpri = rnd.rand() & 0x00ffffff;
        if( _colsec == UMAX )
            _colsec = rnd.rand() & 0x00ffffff;

        if( _nick.is_empty() )
        {
            char* s = _nick.get_append_buf(8);
            for( uint i=0; i<8; )
            {
                char c = rnd.rand() & 0x7f;
                if( (c >= '0' && c <= '9')
                    || (c >= 'a' && c <= 'z')
                    || (c >= 'A' && c <= 'Z') )
                {
                    s[i] = c;
                    ++i;
                }
            }
        }
    }


    PlayerProfile()
    {
        _key_north = 'p';
        _key_south = ';';
        _key_east = '\'';
        _key_west = 'l';

        _key_a = 'a';
        _key_b = 'z';

        _colpri = UMAX;
        _colsec = UMAX;

        _drawratehalf = true;
        _fullscreen = false;

        _ip = "172.16.5.45";
    }

    friend binstream& operator << (binstream& bin, const PlayerProfile& pp )
    {
        bin << pp._nick << pp._colpri << pp._colsec;
        bin << pp._key_north << pp._key_south << pp._key_west << pp._key_east;
        bin << pp._key_a << pp._key_b;

        bin << pp._drawratehalf << pp._fullscreen;
        bin << pp._ip;
        return bin;
    }

    friend binstream& operator >> (binstream& bin, PlayerProfile& pp )
    {
        bin >> pp._nick >> pp._colpri >> pp._colsec;
        bin >> pp._key_north >> pp._key_south >> pp._key_west >> pp._key_east;
        bin >> pp._key_a >> pp._key_b;

        bin >> pp._drawratehalf >> pp._fullscreen;
        bin >> pp._ip;
        return bin;
    }

    friend metastream& operator << (metastream& bin, const PlayerProfile& x )
    {
        MSTRUCT_OPEN(bin, "PlayerProfile");

        MM(bin, "nick", x._nick);
        MM(bin, "color_primary", x._colpri);
        MM(bin, "color_secondary", x._colsec);

        MM(bin, "key_north", x._key_north);
        MM(bin, "key_south", x._key_south);
        MM(bin, "key_west", x._key_west);
        MM(bin, "key_east", x._key_east);
        MM(bin, "key_a", x._key_a);
        MM(bin, "key_b", x._key_b);

        MM(bin, "draw_rate_halve", x._drawratehalf);
        MM(bin, "fullscreen", x._fullscreen);

        MM(bin, "server", x._ip);

        MSTRUCT_CLOSE(bin);
        return bin;
    }
};

////////////////////////////////////////////////////////////////////////////////
struct C_World : World
{
    PlayerProfile _profile;
    
    static int get_coord( coord c )           { return (c.as_int()*TWpx)>>16; }
    
    opcd init();

    opcd assemble_images();
    opcd assemble_background( TexImage& grass );
    opcd load_anim();


    ///@param me client's player
    void draw( const moving* me );

    void draw_moving( const moving& m );
    void draw_flames( bomb* b );

    opcd load_profile( bool create )
    {
        bifstream bif;
        if( bif.open("man.conf") == 0 )
        {
            metastream ms;
            fmtstreamcxx fmt(bif);
            ms.bind_formatting_stream(fmt);

            opcd e;
            try {  e = ms.stream_in(_profile); }
            catch(opcd exc) { e = exc; }
            
            if(!e)
                return e;
        }

        if( create ) {
            _profile.random();
            save_profile();
            return 0;
        }

        return ersNOT_FOUND;
    }

    void save_profile()
    {
        bofstream bif;
        if( bif.open("man.conf") == 0 )
        {
            metastream ms;
            fmtstreamcxx fmt(bif);
            ms.bind_formatting_stream(fmt);

            ms.stream_out(_profile);
        }
    }

    void save_sample()
    {
        //TexImage tex;
        //tex.draw_colorize( _spr_man._array[60], _profile._colpri, _profile._colsec );
    }
    
    enum {
        NMANANIM                        = 2*8,
        MAXFIREFRAMES                   = 64,

        MAX_DEATH_ANIM_FRAMES           = 16,
    };

    ///Gamestate client extension
    enum {
        GSC_INITIAL                     = -1,
        GSC_CONNECT                     = -2,
        GSC_OPTIONS                     = -3,
        GSC_CFGCOLOR                    = -4,
    };


    C_World()
    {
        gs.state = GSC_INITIAL;
        //_gamestate = GSC_CFGCOLOR;
    }


protected:
    void check_colors();
    //opcd colorize_players();

private:

    static void fill_ixtile( ushort* pf, int x, int y )
    {
        DASSERT( x<=XTILES && y<=YTILES );
        pf[0] = x + y*(XTILES+2);
        pf[1] = x+1 + y*(XTILES+2);
        pf[2] = x+1 + (y+1)*(XTILES+2);
        pf[3] = x + (y+1)*(XTILES+2);
    }

    struct moving_sorter
    {
        //return true if a should be drawn before b
        bool operator () (const moving* a, const moving* b) const
        {
            //first sort by z
            if( a->_xyzd0.z < b->_xyzd0.z )  return true;
            if( a->_xyzd0.z > b->_xyzd0.z )  return false;

            //burning stuff is drawn earlier
            if( a->in_fire() ^ b->in_fire() )  return a->in_fire();

            uint ay = a->_xyzd0.y.umod(YTILES<<16);
            uint by = b->_xyzd0.y.umod(YTILES<<16);
            if( ay > by )  return true;
            if( ay < by )  return false;

            if( b == _me )  return true;
            if( a == _me )  return false;
            return false;
        }

        const moving* _me;
    };

    dynarray<moving*> _drawbuf;
    moving_sorter _moving_sorter;

    uint32  _colpri[MEN];                       ///< primary colors
    uint32  _colsec[MEN];                       ///< secondary colors

    uchar   _olddir[MEN];                       ///< previous view directions 
    ushort  _oldflg[MEN];                       ///< previous flags

    struct DrawPos
    {
        int x,y;

        friend binstream& operator << (binstream& bin, const DrawPos& b)   { return bin << b.x << b.y; }
        friend binstream& operator >> (binstream& bin, DrawPos& b)         { return bin >> b.x >> b.y; }
    };

    DrawPos _manpos[(NMANANIM+3)*4];            ///< draw pos for man sprites
    DrawPos _bombpos[(NMANANIM+3)*4];           ///< draw pos for carried bomb

/*
    //source images
    Image _img_bomb;
    Image _img_flame;
    Image _img_man[NMANANIM];
    //Image _img_viewman;

    Image _anim_explode;
*/

public:
    typedef TexImage                        Image;

    typedef TexImage::ColorizedSprite       SpriteFrame;
    typedef TexImage::ColorizedSpriteArray  Sprite;

    Sprite _spr_bomb;
    Sprite _spr_flame;
    Sprite _spr_man;
    Sprite _spr_manq;

    Sprite _aspr_explode;

    Image _img_shadow;

    dynarray<Image> _img_fire;

    //unchanged images
    Image _img_wall;
    Image _img_back;
    //Image _img_hard;
    //Image _img_soft;
    //Image _img_burn;
    Image _img_pwups;

    Image _imgsample_back;

protected:

    void draw_flame( TexImage& dst, const TILE* pt, uint fr, int x, int y )
    {
        if( pt->vcount && pt->hcount )
        {
            const man& m = *_men[ pt->get_vman() ];
            uint32 cp = m.get_colorpri();
            uint32 cs = m.get_colorsec();

            //center
            uint rn = (fr*(x^y))&3;
            dst.draw_colorize( _spr_flame._array[ rn*9*2 ], &_imgsample_back, cp, cs, x, y );
            return;
        }
        
        if( pt->vcount )
        {
            const man& m = *_men[ pt->get_vman() ];
            uint32 cp = m.get_colorpri();
            uint32 cs = m.get_colorsec();

            int vd = (int_abs(pt->vdir)==pt->vcount) ? pt->vdir : 0;

            dst.draw_colorize( get_flame( vd, fr*(x^y), 0 ), &_imgsample_back, cp, cs, x, y );
        }

        if( pt->hcount )
        {
            const man& m = *_men[ pt->get_hman() ];
            uint32 cp = m.get_colorpri();
            uint32 cs = m.get_colorsec();

            int hd = (int_abs(pt->hdir)==pt->hcount) ? pt->hdir : 0;

            dst.draw_colorize( get_flame( -hd, fr*(x^y), 1 ), &_imgsample_back, cp, cs, x, y );
        }
    }

    void draw_powerup( TexImage& dst, const TILE* pt, int x, int y )
    {
        TexImage si;

        uchar t = pt->_pow[0]._type;
        if( t < POWERUP::LAST_POS )
        {
            if(t--)
                si.set( _img_pwups, (t%4)*TWpx, (3-t/4)*TWpx, TWpx, TWpx );
        }
        else if( t == POWERUP::COMBINED )
            si.set( _img_pwups, TWpx, 0, TWpx, TWpx );
        else
            si.set( _img_pwups, 0, 0, TWpx, TWpx );

        dst.draw( si, x, y, TexImage::DRAWMODE_BLIT );
    }

    SpriteFrame& get_flame( int n, uint rn, uint ori )
    {
        //n: >0 top, <0 bottom, 0 body
        uint y;
        if( n < 0 )
            y = 7;
        else if( n > 0 )
            y = 8;
        else
            y = rn%6 + 1;

        uint fr = (rn>>3)&3;

        return _spr_flame._array[ (fr*9 + y)*2 + ori ];
    }

};



#endif //__BMANX_BOMBERLEAGUE_WORLD__HEADER_FILE__
